//
//  main.c
//  temp-test
//
//  Created by 小演员的MacBook on 2023/10/14.
//

#include <math.h>
#include <stdint.h>
#include <inttypes.h>
#include <string.h>
#include <mm_malloc.h>
//#include：导入头文件的预编译指令
#include <stdio.h>//尖括号：去系统指定目录寻找
//通过头文件引入自定义的函数
#include "shui_xian_hua.h"//双引号：先去.c源码文件当前所在的目录寻找，找不到再去系统指定目录寻找；先搜索用户自定义的库，再搜索系统库；

//直接声明方法引入自定义的函数；不过最好还是一个.c对应一个.h，.c中定义、.h中声明；
extern void day_of_year(int year, int month, int day);

void fun(int** q) {
    printf("sizeof q = %ld\n", sizeof(q));
}

char gcs[] = "123";

int main(int argc, const char * argv[]) {
    int a = -5;
    int b = 10;
    printf("-5的十六进制：%#x\n", a);//-5
    printf("10的十六进制：%#x\n", b);//10
    printf("-5与10的按位或的十六进制结果：%#x\n", a | b);//按位或
    printf("-5与10的按位与的十六进制结果：%#x\n", a & b);//按位与
    printf("-5取反的十六进制结果：%#x\n", ~a);//按位取反
    printf("-5与10的按位异或的十六进制结果：%#x\n", a ^ b);//按位异或
    printf("%s\n", "-----");
    //------------------------------------------------------------------------------------------------
    int c = -8;
    printf("原始的十进制值为：%d\n", c);
    printf("原始的十六进制值为：%#x\n", c);
    printf("算术右移2位后的十六进制结果：%#x\n", c >> 2);
    printf("算术右移2位后的十进制结果：%d\n", c >> 2);
    int d = -9;
    printf("原始的十进制值为：%d\n", d);
    printf("原始的十六进制值为：%#x\n", d);
    printf("算术右移2位后的十六进制结果：%#x\n", d >> 2);
    printf("算术右移2位后的十进制结果：%d\n", d >> 2);
    //------------------------------------------------------------------------------------------------
//    char e;
//    scanf("%s", &e);
//    printf("%s", &e);
    //------------------------------------------------------------------------------------------------
    //测试break以及goto
    for (int i = 1; i < 10; i++) {
        for (int j = 1; j < 5; j++) {
            printf("i=%d,j=%d\n", i, j);
            if (j == 2) {
                //break;
                goto temp;
            }
        }
    }
temp:;
    //------------------------------------------------------------------------------------------------
    //声明数组，未初始化数组的情况下，数组中的内容是随机的
    int array[3];
    for (int i = 0; i < 3; i++) {
        printf("%d\n", array[i]);
        printf("%#x\n", array[i]);
        printf("%p\n", &array[i]);
    }
//    printf(array[3]);
    //------------------------------------------------------------------------------------------------
    //统计并打印 水仙花数
    shui_xian_hua(0, 1000);
    //------------------------------------------------------------------------------------------------
    //输出指定日期是属于所在年的第几天
    day_of_year(2023, 11, 15);
    //------------------------------------------------------------------------------------------------
    //字符串、字符数组
    char c1[] = {'z', 'y', 'x'};
    char c3[] = {'z', 'y', 'x', '\0', 'z', 'y', 'x'};
    char c2[] = "zyx\0zyx";
    char c4[4] = "1234";//对于固定长度的字符数组来说，字符缺少的部分会用\0来补全；如果字符串长度超了，编译器也会把多的字符放弃掉，不会导致内存越界的情况；
    printf("%d\n", c2[3]);
    printf("%d\n", c4[3]);
    printf("%c\n", c4[3]);
    printf("%s\n", c2);
    printf("%s\n", c1);
    printf("%s\n", c3);
    printf("%ld\n", sizeof(c2));
    printf("%ld\n", sizeof(c1));
    printf("%ld\n", sizeof(c3));
    printf("c4 = %ld\n", sizeof(c4));
    //------------------------------------------------------------------------------------------------
    /*
     指针：
        *：，&：
        指针变量所占用的内存大小
        注意printf函数左侧%p等控制的是打印的格式（当然%s特殊，可以根据指针中存放的地址再去找到对应存储的字符串），比如长度；而右侧的实际变量则控制的是读取内存的数据，比如int类型就从内存读取4个字节的数据，而指针类型强转更能体现出这个规则；
        为什么说指针需要有类型呢，因为指针中存的是变量的内存首地址，如果没有指针的具体类型，那么在根据指针获取变量的值的时候，如何知道根据指针中存放的变量内存首地址，从内存中读取几个字节，进而获取完整的变量值；
        使用sizeof操作符获取指针指向的变量大小是不正确的。要获取指针所指向的变量的大小，您需要使用该变量的类型来确定其大小。
        请注意，示例中的sizeof(*p)将返回指针本身的大小，而不是指针所指向的变量的大小。要获取指针所指向的变量的大小，您需要使用变量本身的类型（例如sizeof(aa)）。
     */
    long int ap = 0x1234abcd;
    long int* p = &ap;
    int aa = 1;
    p = (long int*)&aa;
    printf("siziof(p) = %ld\n", sizeof(p));
    printf("siziof(*p) = %ld\n", sizeof(*p));//不能通过指针来获取其指向变量类型的大小
//    printf("ap=%p\n", ap);
    printf("p = %#lx\n", *p);
    printf("p = %#lx\n", (long int)p);//p是long int指针类型，因此最好强转成long int再使用%lx打印输出，其实就是将指针类型强转成long int类型，这样地址值就可以参与普通的加减乘除运算了，而不是指针的加减运算（指针的加减运算还需要考虑指针类型，比如int，明明+1，结果实际地址+4）
    printf("&p = %p\n", &p);//打印指针变量的地址
    printf("p = %p\n", p);//打印指针变量中存储的地址
    //------------------------------------------------------------------------------------------------
    //将指针强转成整型，然后计算实际的地址差
    long int pp = (long int)p;
    long int ppp = (long int)(p + 1);
    printf("%ld\n", ppp - pp);//得到的结果是8，因为long int占用8个字节，这就表明long int类型指针+1，实际地址是+8，和具体的指针类型有关；
    //------------------------------------------------------------------------------------------------
    //指针数组存的就是地址，但是%s、%p等可以控制具体取值行为。比如%p得到的是指针数组中的实际内容，即地址；%s得到的就是指针数组中地址对应的内存块中的内容；
    char* name[3] = {"123", "456", "789"};
    printf("name=%p\n", name);//name是指针数组名，数组的名字是常量，代表的是这个数组的首地址，因此name并不是name[0]指针中存放的地址，而是name[0]指针本身所占用的内存块地址；
    printf("*name=%p\n", *name);//根据name表示的地址（即name[0]指针本身所占用的内存块地址）获取到的内容就是name[0]指针中存放的地址，本例中就是字符串"123"的首地址；
    printf("name[0]存的地址：%p\n", name[0]);//%p打印的就是name[0]实际的指针中存放的地址，本例中就是字符串"123"的首地址；
    printf("name[1]存的地址：%p\n", name[1]);//%p打印的就是name[1]实际的指针中存放的地址，本例中就是字符串"456"的首地址；
    printf("name[0]存的地址对应的字符串：%s\n", name[0]);//虽然name[0]存放的是地址，但是%s比较特殊，会根据指针中存放的地址，继续寻找到对应的字符串（我理解只能向下寻找一层）；"123"
    printf("name[1]存的地址对应的字符串：%s\n", name[1]);//虽然name[1]存放的是地址，但是%s比较特殊，会根据指针中存放的地址，继续寻找到对应的字符串（我理解只能向下寻找一层）；"456"
    printf("name[0] + 1的值：%p\n", name[0] + 1);//因为name[0]指针为char类型，因此+1是向后移动1个字节；如果是int类型，那么+1就是向后移动4个字节；
    printf("name[0] + 1的值对应的字符串：%s\n", name[0] + 1);//由于遇到了\0，因此中止查询，得到的结果就是"23"；
    printf("用*对name[0]取内容：%c\n", *(name[0]));//name是char类型指针，用*获取内容也是获取的1个字节的char字符，因此需要用%c获取单个字符，得到的结果是1；注意这里不能用%s；
    //------------------------------------------------------------------------------------------------
    //多级指针
    int a1 = 0x12345678;
    int* p1 = &a1;
    int** q1 = &p1;
    int*** m1 = &q1;
    printf("a1 = %#x\n", a1);
    printf("*p1 = %#x\n", *p1);
    printf("**q1 = %#x\n", **q1);
    printf("***m1 = %#x\n", ***m1);
    //------------------------------------------------------------------------------------------------
    //指针-字符串-文字常量区
    char* s = "12345678";
    printf("第一位是字符：%c\n", s[0]);
//    s[0] = 'a';//常量区的内容不可以被修改
    printf("s字符：%s\n", s);
    //------------------------------------------------------------------------------------------------
    //指针-字符串-堆区
    char* ss = (char*)malloc(15);
    strcpy(ss, "hello world");
    printf("%s\n", ss);
    //------------------------------------------------------------------------------------------------
    //数组指针
    int a3[3][5][4];
    int (*p3)[5][4];
    p3 = a3;
    printf("a3 = %p\n", a3);
    printf("a3 + 1 = %p\n", a3 + 1);
    printf("a3[1] = %p\n", a3[1]);
    printf("a3[1][0] = %p\n", a3[1][0]);
    printf("a3[1][1] = %p\n", a3[1][1]);
    printf("p3 = %p\n", p3);
    printf("&p3 = %p\n", &p3);//指针的指针,即二级指针
    printf("p3 + 1 = %p\n", p3 + 1);
    printf("p3[1] = %p\n", p3[1]);
    printf("p3[1][0] = %p\n", p3[1][0]);
    printf("p3[1][1] = %p\n", p3[1][1]);
    int a4[4][5];
//    printf("a[0][0] = %p\n", a4[0][0]);
    printf("a4 = %p\n", a4);//指针指向的类型是int(*)[5]（5*4）
    printf("&a4 = %p\n", &a4);//指针指向的类型是int(*)[4][5]（4*5*4）——相当于int(*p)[4][5]
    printf("a4[0] = %p\n", a4[0]);//指针指向的类型是int（1*4）
    printf("&a4[0][0] = %p\n", &a4[0][0]);//指针指向的类型是int（1*4）
    printf("&a4 + 1 = %p\n", &a4 + 1);//指针指向的类型是int(*)[4][5]（4*5*4）——相当于int(*p)[4][5]
    printf("a4 + 1 = %p\n", a4 + 1);//指针指向的类型是int(*)[5]（5*4）
    printf("a4[0] + 1 = %p\n", a4[0] + 1);//指针指向的类型是int（1*4）
    printf("&a4[0][0] + 1 = %p\n", &a4[0][0] + 1);//指针指向的类型是int（1*4）
    //------------------------------------------------------------------------------------------------
    //指针数组 函数入参
    int* p5[5];
    fun(p5);
    printf("sizeof p5 = %ld\n", sizeof(p5));
    //------------------------------------------------------------------------------------------------
    //C语言将字符常量视为int类型而非char类型；'asdf'为int类型常量，编译器转换成char类型，只取最后一位f;
//    char c6 = 'asdf';
//    printf("%c-%d\n", c6, c6);
    //------------------------------------------------------------------------------------------------
    //stdint.h、inttypes.h 主要考虑可移植性问题
    int32_t a7 = 10;
    printf("123-%"PRId32"-456""\n", a7);
    //------------------------------------------------------------------------------------------------
    //
    char s8[20] = "zhang";
    char* p8 = "zhang";
    printf("s8:sizeof = %zd-strlen = %zd\n", sizeof s8, strlen(s8));
    printf("p8:sizeof = %zd-strlen = %zd\n", sizeof p8, strlen(p8));
    //------------------------------------------------------------------------------------------------
    //浮点型精度
    printf("%lf\n", pow(10, 16) + 1);
    printf("%lf\n", pow(10, 16) + 10);
    //------------------------------------------------------------------------------------------------
    //逗号运算符
    int a9;
    a9 = 1,2;
    int b9 = (1,2);
    printf("a9 = %d\n", a9);
    printf("b9 = %d\n", b9);
    //------------------------------------------------------------------------------------------------
    //结构体：构造类型，数组也是一种构造类型；
    struct temp {
        int i;
        int j;
    };
    typedef struct temp temp1;
    temp1 temp = {1,2};
    printf("struct temp = {i = %d, j = %d}\n", temp.i, temp.j);
    temp.i = 3;
    printf("struct temp = {i = %d, j = %d}\n", temp.i, temp.j);
    typedef struct {int i; int j;} STU;
    STU temp2;
    temp2.i = 4;
    printf("struct temp = {i = %d, j = %d}\n", temp2.i, temp2.j);
    STU* pStu = &temp2;
    printf("struct temp = {i = %d, j = %d}\n", (*pStu).i, (*pStu).j);
    printf("struct temp = {i = %d, j = %d}\n", pStu -> i, pStu -> j);
    struct stu {
        char a;
        int i;
    } zs;
    printf("占用字节数：%zd\n", sizeof(zs));
    //指定对其规则；编译器默认规则是以最大的类型字节数开辟内存；指定以2个字节为单位开辟内存（两者比较取最小值，包括成员的起始地址倍数规则）；
//#pragma pack(2)
    struct stu1 {
        char a;
        int i;
    } zs1;
    printf("占用字节数：%zd\n", sizeof(zs1));
    struct stu2 {
        unsigned int a;
        unsigned int b;
        unsigned int c;
        unsigned int d;
        unsigned int i;
    } zs2;
    printf("占用字节数：%zd\n", sizeof(zs2));
    //位段（位域），指定成员占用的位数;位段的长度不能大于存储单元的长度，比如char类型位段不能大于8位，int类型位段不能大于32位；
    struct stu3 {
        unsigned int a : 2;
        unsigned int b : 6;
        unsigned int c : 4;
        unsigned int d : 4;
        unsigned int i;
    } zs3;
    printf("占用字节数：%zd\n", sizeof(zs3));
    //一个位段必须存放在一个存储单元中，不能跨两个单元。第一个单元空间不能容纳下一个位段，则该空间不用，而从下一个单元起存放该位段；
    struct stu4 {
        char a : 7;
        char b : 7;
        char c : 2;
    } zs4;
    printf("占用字节数：%zd\n", sizeof(zs4));//占用3个字节，并不是占用2个字节，位段不能跨存储单元；
    struct stu5 {
        char a : 1;
        char b : 2;
        char : 0;//表达的含义是c不和b存在同一个存储单元中
        char c : 3;
    } zs5;
    printf("占用字节数：%zd\n", sizeof(zs5));//占用2个字节，并不是占用1个字节；
    struct stu6 {
        char a : 1;
        char b : 2;
        char : 2;//浪费2位不用
        char c : 3;
    } zs6;
    printf("占用字节数：%zd\n", sizeof(zs6));//占用1个字节，如果浪费3为不用，那就是占用2个字节了，因为位段不能跨存储单元；
    //------------------------------------------------------------------------------------------------
    //共用体：构造类型；共用体所有成员占有同一段地址空间；共用体的大小是其占内存长度最大的成员大小；应用场景：几个不同的成员，公用同一块内存；
    union temp11 {
        int a;
        char b;
    } ls;
    printf("占用字节数：%zd\n", sizeof(ls));//成员a和b公用同一块地址，指针指向同一块内存；
    //------------------------------------------------------------------------------------------------
    //枚举，枚举元素本身由系统定义了一个表示序号的数值，默认是从0开始顺序定义为0，1，2……；枚举值是常量，不能再程序中用赋值语句再次赋值；
    enum meiju {
        aaa,bbb=2,ccc
    } meiju1;
    meiju1 = aaa;
    printf("枚举：meiju1=%d\n", meiju1);
    //const
    int const *cp;//指针指向的内存区域内容不能修改，指针cp存储的内存地址可以修改；
    const int *cp1;//同上
    int * const cp2;//cp2存储的内存地址不能修改，cp2指向的内存区域内容可以修改；
    int * const *cp3;//cp3指针指向的内存区域内容不能修改，但是cp3存储的内存地址可以修改，*cp存储的内存地址可以修改；
    int i = 2;
    int *cp4;
    cp4 = &i;
    cp3 = &cp4;
//    *cp3 = &cp4;//*cp不能修改
    **cp3 = 3;
    cp3 = &cp4;
    
    char cs[] = "123";
    char *sp = "123";
    cs[1] = '3';
    gcs[1] = '3';
    printf("%c\n", cs[1]);
    printf("%c\n", gcs[1]);
//    *sp = '1';//sp指针指向数据段，数据段只读不可修改；
    
    
    
    
    
    
    
    
    
    
    return 0;
}
